package controller.gui;

import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.util.List;
import java.util.jar.JarEntry;

import assister.ClassAssister;

import mapelements.MapElement;
import mapelements.Renderer;

import tool.MainConstructorNotFoundException;
import tool.classloader.FileNotSupportedException;
import tool.classloader.JarClassLoader;
import tool.classloader.JarExaminator;
import tool.classloader.LocalObjectClassLoader;
import tool.dialog.JConstructorDialog;
import tool.dialog.JConstructorUpgradedDialog;
import controller.BrowseUser;
import controller.Controller;
import controller.NullStaticObjectException;
import controller.StandardIOController;
import controller.MapElementToAddConveyor;
import controller.states.MapElementToAddStateUser;
import gui.BoxElement;
import gui.MapEditorFrame;
import gui.SelectionBox;

/**rappresenta il Controller che rileva gli eventi della
 * SelectionBox, cioe' di quel componente che contiene
 * i nomi (e le relative immagini) delle classi che 
 * possono essere istanzializzate sulla mappa.
 * 
 * <p>Il Controller non deve essere forzato alla acquisizione
 * di un unico tipo di classe (per esempio Renderer, Concept),
 * ma deve essere abbastanza generale da poter consentire
 * successivi wizard personalizzati per l'inserimento
 * della classe nella mappa
 * 
 * 
 * @author Koldar
 * @version 1.4
 */
public abstract class SelectionBoxController extends StandardIOController implements MapElementToAddStateUser,MapElementToAddConveyor,BrowseUser{ 
	
	/**rappresenta il ClassLoader in grado di caricare gli oggetti dalla mappa*/
	protected JarClassLoader mapElementLoader;
	/**indica l'oggetto che esamina il jar per riempire la SelectionBox circa le entry*/
	protected JarExaminator examinator;
	/**rappresenta la selectionBox pilotata dal Controller*/
	protected SelectionBox listenedBox;
	/**rappresenta l'applicazione con cui viene cabalto il controller*/
	private Frame mapFrame;
	/**rappresenta il controller che deve essere informato riguardo la scelta del Renderer/Concept da inserire nella mappa*/
	protected InsertionDisplayController insertionDisplayController;
	/**indica la classe che ogni entry nella SelectionBox deve estendere*/
	private Class classFather;
	
	public SelectionBoxController(Frame frame,SelectionBox boxwired,InsertionDisplayController displaycontroller,Class<?> _classFather){
		super();
		this.mapFrame=frame;
		this.listenedBox=boxwired;
		this.classFather=_classFather;
		this.insertionDisplayController=displaycontroller;
		this.addStandardIOListener(this.listenedBox);
	}
	
	/**sincronizza il {@link #mapElementLoader} nella classe in modo che possa rilevare le classi interessanti per il SelectionBox {@link #listenedBox}
	 * 
	 * @param directory la directory da far analizzare al {@link #mapElementLoader}
	 */
	public void sinchronizeClassLoader(File directory){
		try {
			this.mapElementLoader=new JarClassLoader(directory);
			this.examinator=new JarExaminator(directory);
		} catch (IOException error) {
			this.log.setText(error.getMessage());
		}
	}
	
	/**compila e riempie il {@link #listenedBox} con i risultati offerti dal {@link #mapElementLoader}
	 * 
	 */
	public void sinchronizeSelectionBox(){
		this.listenedBox.removeAllEntries();
		List<JarEntry> list=this.examinator.getList(this.CLASSFILTER);
		for (JarEntry j:list){
			try{
				String name=j.getName().replace(".class","").replaceAll("/|\\\\",".");
				Class<?> cl=Class.forName(name,true,this.mapElementLoader);
				if (ClassAssister.isSuperclass(classFather, cl)){
					this.addToSelectionBox(name, cl);
				}
			} catch (ClassNotFoundException e) {
				Controller.log.setText("the class \""+ j.getName() +"\" doesn't have any accessible empty constructor");
			}
		}
		//TODO soluzione fatta a caso che però funge!
		this.listenedBox.getParent().repaint();
		this.listenedBox.getParent().validate();
	}
	
	/**indica come deve essere inserita la nuova classe dentro la selectionBox
	 * 
	 * @param name il nome della classe
	 * @param clazz la classe da inserire
	 */
	protected abstract void addToSelectionBox(String name,Class clazz);
	
	/**indica come deve essere passata la classe scelta dall'utente al DisplayController che (eventualmente) la inserirà
	 * nella mappa
	 * 
	 * @param classToTransfer la classe da spedire al DisplayController affinché possa essere sperabilmente inserita nella mappa
	 */
	protected abstract void transferClassToDisplayController(Class<?> classToTransfer);

	//TODO ma scusa... al posto di passare in arg0 solo il nome della classe, non si potrebbe passare l'intero path assoluto??? in questo modo non dovremmo star lì a passare i vari controller!!!
	
	/**controlla dove l'utente ha effettuato un double click; se tale double click è stato realizzato in un Entry di una SelectionBox allora
	 * comunica la classe Concept/Renderer voluta dall'utente al DisplayController. Da notar eche qui viene soltanto <strong>scelta</strong> la classe da inserire, non
	 * viene affatto istanzializzata.
	 * 
	 */
	@Override
	public void doubleClick(MouseEvent arg0) {
		//***************** reperimento della classe da utilizzare **************************
			Class<?> classloaded=((BoxElement)arg0.getSource()).getClassContained();
		//***************** comunicazione della classe scelta al DisplayController *******************
			this.transferClassToDisplayController(classloaded);
	}

	@Override
	public void leftClick(MouseEvent arg0) {
	}

	@Override
	public void rightClick(MouseEvent arg0) {
	}

	@Override
	public void unknownClick(MouseEvent arg0) {
	}

	@Override
	public void enterTyped(KeyEvent arg0) {
	}

	@Override
	public void escTyped(KeyEvent arg0) {
	}

	@Override
	public void unkwnownTyped(KeyEvent arg0) {
	}
	
	

}
